#ifndef PW_PBL_DST_HPP
#define PW_PBL_DST_HPP

#include <array>
#include <string_view>
#include <utility>

namespace pw_pbl
{
	template <typename ELEMENT>
	struct SliceDst;

	template <typename IMP>
	struct DstPtr;

	template <typename IMP>
	concept ccIsSliceDst = requires { SliceDst{std::declval<IMP>()}; };
} // namespace pw_pbl

namespace pw_pbl::_dst
{
	template <typename T>
	struct Traits : public Traits<decltype(SliceDst{std::declval<T>()})>
	{};

	template <typename ELEMENT>
	struct Traits<SliceDst<ELEMENT>>
	{
		using Element = ELEMENT;
	};

	template <ccIsSliceDst TAIL>
	struct Traits<SliceDst<TAIL>>
	{
		using Element = Traits<TAIL>::Element;
	};

	template <typename IMP, size_t N>
	struct FullObj : public IMP
	{
		IMP header;
		std::array<typename Traits<IMP>::Element, N> tailer;

		constexpr operator IMP &() { return header; }
		constexpr operator const IMP &() const { return header; }
		constexpr operator IMP &&() && { return header; }
		constexpr operator const IMP &&() const && { return header; }
	};
} // namespace pw_pbl::_dst

namespace pw_pbl
{

	template <typename ELEMENT>
	struct SliceDst
	{
		using element_type = ELEMENT;
		template <std::derived_from<SliceDst> IMP, size_t N>
		static constexpr auto from_raw_part(const IMP &header, const std::array<element_type, N> &data) { return _dst::FullObj<IMP, N>{header, data}; }
	};

	template <ccIsSliceDst TAIL>
	struct SliceDst<TAIL>
	{
		using element_type = typename _dst::Traits<TAIL>::Element;
		template <std::derived_from<SliceDst> IMP, size_t N>
		static constexpr auto from_raw_part(const IMP &header, const std::array<element_type, N> &data) { return _dst::FullObj<IMP, N>{header, data}; }
	};

	template <typename IMP>
	struct DstPtr
	{
		IMP *ptr;
		constexpr operator bool() const { return ptr != nullptr; }

		explicit constexpr DstPtr(IMP &header) : ptr(&header) {}
		explicit inline DstPtr(std::string_view view) : ptr(view.size() == sizeof(IMP) ? (IMP *)view.data() : nullptr) {}
	};

	template <ccIsSliceDst IMP>
	struct DstPtr<IMP>
	{
		IMP *ptr;
		size_t elementCnt;
		constexpr operator bool() const { return ptr != nullptr; }

		explicit constexpr DstPtr(IMP &header, size_t n) : ptr(&header), elementCnt(n) {}
		explicit inline DstPtr(std::string_view view)
		{
			if (view.size() >= sizeof(IMP))
			{
				using ssize_t = std::make_signed_t<size_t>;
				auto re = std::div((ssize_t)view.size() - sizeof(IMP), (ssize_t)sizeof(typename _dst::Traits<IMP>::Element));
				if (re.rem == 0)
				{
					ptr = (IMP *)view.data(), elementCnt = re.quot;
					return;
				}
			}
			ptr = nullptr, elementCnt = 0;
		}
		template <size_t N>
		constexpr DstPtr(_dst::FullObj<IMP, N> &obj) : ptr(obj.header), elementCnt(N) {}
	};
} // namespace pw_pbl

#endif // PW_PBL_DST_HPP
